package com.rc.saas.mini.minip.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.rc.saas.mini.common.base.TResponse;
import com.rc.saas.mini.common.constant.HttpConstant;
import com.rc.saas.mini.common.constant.SessionConstants;
import com.rc.saas.mini.minip.token.RedisTokenLoader;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;

/**
 * 令牌拦截器
 * Created by sven on 2018-02-16 13:29
 */
public class TokenInterceptor extends HandlerInterceptorAdapter {
    private static Logger logger = LogManager.getLogger(TokenInterceptor.class);

    @Autowired
    private RedisTokenLoader redisTokenLoader;

    /**
     * Controller之前执行
     * preHandle：拦截于请求刚进入时，进行判断，需要boolean返回值，如果返回true将继续执行，如果返回false，将不进行执行。一般用于登录校验
     * 1.当preHandle方法返回false时，从当前拦截器往回执行所有拦截器的afterCompletion方法，再退出拦截器链。也就是说，请求不继续往下传了，直接沿着来的链往回跑。
     * 2.当preHandle方法全为true时，执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的Controller。然后进入拦截器链，运行所有拦截器的postHandle方法,完后从最后一个拦截器往回执行所有拦截器的afterCompletion方法.
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getServletPath();
        String osType = request.getHeader(HttpConstant.OS_TYPE);
        String ip = request.getHeader("x-forwarded-for");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type,token");
        response.addHeader("Access-Control-Allow-Methods", "POST");
        response.addHeader("Access-Control-Max-Age", "1000");
        logger.info("令牌拦截器.preHandle,ip:{},osType;{},url:{}", ip, osType, url);

        //如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        //客户端将token封装在请求头中，格式为（Bearer后加空格）：Authorization：Bearer+token
        //如果未添加认证信息或者认证信息错误，返回没有权限的错误信息
        String authorization = request.getHeader(HttpConstant.AUTHORIZATION);
        if (authorization == null || !authorization.startsWith(HttpConstant.AUTHORIZATION_PREFIX)) {
            logger.error("preHandle,osType:{},no Authorization:{}", osType, url);
            responseResult(response, response.getWriter(), TResponse.SC_FORBIDDEN());
            return false;
        }

        HttpSession httpSession = request.getSession();
        //去除Bearer 后部分
        final String token = authorization.substring(7);
        //验证token是否存在缓存中
        if (redisTokenLoader.verifyToken(token)) {
            httpSession.setAttribute(SessionConstants.SESSION_TOKEN, token);
            //如果token验证成功，将token对应的用户id存在request中，便于在controller中使用
            //如果SESSION已有，就用当前的，否则就创建一个新的
            if("mini".equals(osType)){
                //小程序用户
                httpSession.setAttribute(HttpConstant.SESSION_TOKEN, token);
            }else if("wechat".equals(osType)){
                httpSession.setAttribute(HttpConstant.SESSION_WECHAT_TOKEN, token);
            }else if("app".equals(osType)){
                httpSession.setAttribute(HttpConstant.SESSION_APP_TOKEN, token);
            }else{
                //请求不通过，返回错误信息给客户端
                responseResult(response, response.getWriter(), TResponse.FAIL("headers osType not found"));
                return false;
            }
            logger.info("成功保存TOKEN在SESSION中,Token:{},HttpSessionId:{}", token, httpSession.getId());
            return true;
        } else {
            //请求不通过，返回错误信息给客户端
            responseResult(response, response.getWriter(), TResponse.SC_FORBIDDEN());
            return false;
        }
    }

    /**
     * 返回信息给客户端
     * @param response
     * @param out
     * @param tResponse
     */
    private void responseResult(HttpServletResponse response, PrintWriter out, TResponse tResponse) {
        response.setContentType(HttpConstant.CONTENT_TYPE_JSON);
        String json = JSONObject.toJSONString(tResponse);
        out.print(json);
        out.flush();
        out.close();
    }
}
